function handleLowMemory( event ) 
  print( "memory warning received!" ) 
end 

system.activate( "multitouch" )

local physics = require "physics"
physics.start()
physics.setPositionIterations(128)
--physics.setVelocityIterations( 12 )
--physics.setDrawMode( "debug" ) -- shows collision engine outlines only
--physics.setDrawMode( "hybrid" ) -- overlays collision outlines on normal Corona objects
physics.setDrawMode( "normal" ) -- the default Corona renderer, with no collision outli-nes

----------------------------------------------------------------------
-- Global val
----------------------------------------------------------------------
-- Virtual screen size
local screenWidth = 480
local screenHeight = 320

-- Playable size
local playableHeight = 640
local playableCurrentHeight = 0

local playableUpBorder = 80
local playableDownBorder = 280
local playableHeightController
-- background sky
local b1
local b2

-- balloon 
local balloon
local balloonNo
local ropeLen = 100
local link = {}
local myJoints = {}
local jointCount = 10
local ballonMaxOrient = 40
-- HUD
local scoreText

local dieText
-- Gameplay
local isControl = false
local controlTouchID = 0;
local speed = 3;
local travelDistance = 0;
local fps = 30
local isDied = false
local forceControlFactor = 0.6;


local spawnMinTime = 1000;
local spawnMaxTime = 1000;
local spawnMinCount = 1;
local spawnMaxCount = 3;

-- Obj
local enemyGroup 
-- Internal use
local currentBkCount = 0;
--local stage = display.getCurrentStage()
local gameScreen
local SpriteScreen
local handx = 0
local handy = 0
local deviceX = 0
local deviceY = 0
local controlActor

----------------------------------------------------------------------
-- End of Global val
----------------------------------------------------------------------

----------------------------------------------------------------------
-- Load background
----------------------------------------------------------------------
function readLayerFile()
	-- Read Layer data
	local file_name = string.format("%d.layer",1)
	local layer_path = system.pathForFile( file_name, system.DocumentsDirectory )
	local layer_fin = io.open( layer_path, "r" )
	if(layer_fin==nil) then
		return
	end
	while true do	
		local name_label,colon,layer_name = layer_fin:read(4,1,"*l")
		local speed_label,colon2,speed = layer_fin:read(5,1,"*n","*l")
		layer_fin:read("*l");
		
		if(name_label==nil) then
			break;
		end
		
		local layer = display.newGroup()
		layer.egName = layer_name
		--print(layer_name," ",speed)
		layer.egSpeed = speed
		gameScreen:insert(layer)		
		if(layer_name=="MainLayer") then			
			gameScreen.MainLayer = layer
		end		
		if(layer_name=="Sky") then			
			gameScreen.Sky = layer
		end
		
	end
	layer_fin:close()
end

function createSpriteInstance(sprite_name,x,y)
	-- Find sprite
	local sprite = nil
	for i = 1, SpriteScreen.numChildren do	
		if( SpriteScreen[i].egName and sprite_name == SpriteScreen[i].egName ) then
			sprite = SpriteScreen[i]			
			break
		end
	end
	if( sprite==nil) then
		print("ERROR! Khong tim` thay sprite co ten la ",sprite_name)
		return
	end
	-- Create instance
	local layer_name = sprite.egLayer
	--print(sprite.egLayer)
	for i = 1, gameScreen.numChildren do
		if( gameScreen[i].egName == layer_name ) then
			local sprite_intance = display.newImage( sprite.egPath)
			sprite_intance.x = x 
			sprite_intance.y = y			
			gameScreen[i]:insert(sprite_intance)			
			return sprite_intance
		end
	end
	
end

function readSpriteFile()
	local file_name = string.format("%d.sprite",1)
	local sprite_path = system.pathForFile( file_name, system.DocumentsDirectory )
	local sprite_fin = io.open( sprite_path, "r" )
	local sprite_count = 0;
	if(sprite_fin==nil) then
		return
	end
	while true do	
		local name_label,colon,sprite_name = sprite_fin:read(4,1,"*l")
		local type_label,colon2,type_value = sprite_fin:read(4,1,"*l")
		local path_label,colon3,path_value = sprite_fin:read(4,1,"*l")
		local layer_label,colon4,layer_value = sprite_fin:read(5,1,"*l")	
		sprite_fin:read("*l");	
		if(name_label==nil) then
			break;
		end
		
		local sprite = display.newGroup()	
		sprite.x = 50 + math.fmod(sprite_count,4) * 210
		sprite.y = 80 + math.floor(sprite_count/4)* 50
		sprite.egName = sprite_name
		sprite.egType = type_value	
		sprite.egPath = path_value
		sprite.egLayer = layer_value
		
		-- DEBUG PRINT
		--print(name_label,colon,sprite_name)
		-- print(type_label,colon2,type_value)
		-- print(path_label,colon3,path_value)
		-- print(layer_label,colon4,layer_value)
		-- print("")
		
		SpriteScreen:insert(sprite);		
		sprite_count = sprite_count+1
		
	end
	sprite_fin:close()
end

function loadBackground()	
	readLayerFile()
	readSpriteFile()	
	
	travelDistance = 0
	
	local file_name = string.format("%d.level",1)
	local file_path = system.pathForFile( file_name, system.DocumentsDirectory )
	local level_fin =  io.open( file_path, "r" )	
	
	if(level_fin==nil) then
		return
	end
	local nothing = level_fin:read("*n","*l")
	while true do	
		local egName = level_fin:read("*l")
		local x = level_fin:read("*n","*l")
		local y = level_fin:read("*n","*l")
		local rotation = level_fin:read("*n","*l")
		local xScale = level_fin:read("*n","*l")
		local yScale = level_fin:read("*n","*l")
		if(egName==nil) then
			break;
		end
		local sprite_instance = createSpriteInstance(egName,x,y)
		sprite_instance.rotation = rotation
		sprite_instance.xScale = xScale
		sprite_instance.yScale = yScale		
		--print(egName," ",x," ",y," ")
		--print(rotation," ",xScale," ",yScale)		
	end
	level_fin:close()
end

----------------------------------------------------------------------
-- Init Level Function
----------------------------------------------------------------------
function initLevel()
	-- Init global val
	gameScreen = display.newGroup()	
	SpriteScreen = display.newGroup()	
	SpriteScreen.isVisible = false
	isControl = false
	speed = 3;
	travelDistance = 0;
	fps = 30
	isDied = false
	gameScreen.x = 0
	
	currentBkCount = 0;
	handx = 0
	handy = 0	
	
	loadBackground()
	
	-- Temp background
	b1 = display.newImage( "Resource/sky.jpg", -300, 0 )
	gameScreen.Sky:insert(b1)
	
	--gameScreen.MainLayer = display.newGroup();
	-- create balloon
	local BalloonCollisionFilter = { categoryBits = 2, maskBits = 0 } 

	balloon = display.newGroup();
	balloon.x = screenWidth/2;
	balloon.y = screenHeight/2;
	balloon.normal = display.newImage( "Resource/balloon.png", -32, -32 )	
	balloon.normal.isVisible = true
	balloon.freeze = display.newImage( "Resource/balloon_ice.png", -32, -32 )
	balloon.freeze.isVisible = false
	balloon:insert(balloon.normal);
	balloon:insert(balloon.freeze);
	
	gameScreen.MainLayer:insert(balloon)	
	balloon.status = "normal"
	balloon.radius = 32	
	----------------------------------------------------------------------
	-- Balloon function
	---------------------------------------------------------------------
	function balloon:update()
		
		if( balloon.status == "normal" ) then
			balloon:applyForce( 0, -83, balloon.x, balloon.y )		
			-- Fix orient			
			local rot = balloon.rotation
			rot = math.fmod (rot, 360)

			if( rot < ballonMaxOrient and rot > -ballonMaxOrient ) then
				balloon.isFixedRotation=false
			else 
				if(balloon.isFixedRotation==false) then
					balloon.isFixedRotation=true
					balloon.angularVelocity = 0
					if( rot > 0 ) then
						balloon.rotation = ballonMaxOrient-5
					else
						balloon.rotation = -(ballonMaxOrient-5)
					end
				end
			end			
		end
		
		if( balloon.status == "freeze" ) then
			balloon:applyForce( 0, 83, balloon.x, balloon.y )

			balloon.freezeTimer = balloon.freezeTimer - 1/30;
			if(balloon.freezeTimer<=0.) then
				balloon:setStatus("normal")
			end
		end		
	end	
	
	function balloon:onTouchEnemy(enemy)
		if( enemy.type == "snow" ) then
			
			if(balloon.status=="normal") then
				balloon:setStatus("freeze")
				balloon.freezeTimer = 8;
			else
				onDie()
			end
			enemy.status = "deleted"
		end
		
		if( enemy.type == "bird" ) then	
			onDie()
		end
	end	
	
	function balloon:setStatus(new_status)
	
		if( new_status == "normal" ) then
			balloon.isFloat =true
			balloon.normal.isVisible = true
			balloon.freeze.isVisible = false
		end
		
		if( new_status == "freeze" ) then
			balloon.isFloat =false
			balloon.isFixedRotation=false			
			balloon.normal.isVisible = false
			balloon.freeze.isVisible = true
		end	
		
		balloon.status = new_status;
	end
	
	function balloon:onDie()
		isDied = true
		dieText = display.newText("DIE", travelDistance+ 400,250, native.systemFont, 100)
		dieText:setTextColor(255, 0, 0)
		if(controlActor.tempJoint) then
			controlActor.tempJoint:removeSelf()
			controlActor.tempJoint = nil
		end
		timer.performWithDelay(2000, resetCheckPoint )	
	end
	
	physics.addBody( balloon, { density = 0.5, friction = 0.3, bounce = 0.2, radius = 25, filter=BalloonCollisionFilter } )

	local RopeCollisionFilter = { categoryBits = 1, maskBits = 0 } 
	for j = 1,jointCount do
		link[j] = display.newImage( "Resource/rope.png" )
		gameScreen.MainLayer:insert(link[j])
		link[j].x = balloon.x
		link[j].y = balloon.y +(balloon.height/2) +(j*6)
		physics.addBody( link[j], { density=20.0, friction=0, bounce=0 , filter=RopeCollisionFilter} )
		
		--Create joints between links
		if (j > 1) then
			prevLink = link[j-1] 
		else
			prevLink = balloon 
		end
		myJoints[#myJoints + 1] = physics.newJoint( "pivot", prevLink, link[j], link[j].x , link[j].y - 3 )

	end
	
	balloonNo = display.newImage( "Resource/no.png", link[jointCount].x - 16, link[jointCount].y - 16 )
	gameScreen.MainLayer:insert(balloonNo)
	physics.addBody( balloonNo, { density = 1, friction = 0.3, bounce = 0.2, radius = 25, filter=RopeCollisionFilter } )
	myJoints[#myJoints + 1] = physics.newJoint( "pivot", link[jointCount], balloonNo, link[jointCount].x , link[jointCount].y )
	
	controlActor = balloonNo
	
	-- Playable Height
	playableHeightController = balloonNo
	playableCurrentHeight = 0
	
	-- objs
	enemyGroup = display.newGroup()	
	gameScreen.MainLayer:insert(enemyGroup);
	-- HUD
	scoreText = display.newText("0", 80, 10, native.systemFont, 30)
	scoreText:setTextColor(255, 0, 0)
end

----------------------------------------------------------------------
-- Reset to CheckPoint Function
----------------------------------------------------------------------
function resetCheckPoint()
	-- Remove all display obj	
	for i = 1, gameScreen.numChildren do		
		gameScreen[1]:removeSelf( )
	end
	gameScreen:removeSelf()
	
	-- HUD
	dieText:removeSelf()
	scoreText:removeSelf()
	
	-- Recreate
	initLevel()
end

----------------------------------------------------------------------
-- onDie Function
----------------------------------------------------------------------
function onDie()
	isDied = true
	isControl = false
	dieText = display.newText("DIE", travelDistance+ 400,250, native.systemFont, 100)
	dieText:setTextColor(255, 0, 0)
	if(controlActor.tempJoint) then
		controlActor.tempJoint:removeSelf()
		controlActor.tempJoint = nil
	end
	timer.performWithDelay(2000, resetCheckPoint )	
end

----------------------------------------------------------------------
-- updateGameScreen Function
----------------------------------------------------------------------
function updateGameScreen()
	for i = 1, gameScreen.numChildren do		
		gameScreen[i].x = -travelDistance*gameScreen[i].egSpeed
		gameScreen[i].y = -playableCurrentHeight
	end
end
----------------------------------------------------------------------
-- Enemy Function
----------------------------------------------------------------------
function newEnemy(params)
	local enemy
	-------------------------------------------------------------------
	-- SNOW
	-------------------------------------------------------------------
	if( params.type == "snow") then
		enemy = display.newImage( "Resource/snowflake.png",params.x,params.y )
		enemy.type = params.type
		enemy.radius = 8;
		enemy.status = "normal"
		enemyGroup:insert(enemy)
		-- public func
		function enemy:update()
			enemy.x = enemy.x- 3
			local offsetX = enemy.x - balloon.x;
			local offsetY = enemy.y - balloon.y;
			local totalRadius = enemy.radius + balloon.radius
			if(  (offsetX*offsetX + offsetY*offsetY) < totalRadius*totalRadius ) then
				balloon:onTouchEnemy(enemy)
			end	

			if(enemy.x < travelDistance-100) then
				enemy.status = "deleted"
			end
		end
		
		function enemy:onTouch(event)
			if( event.phase =="began") then
				local offsetX = enemy.x - event.x;
				local offsetY = enemy.y - event.y;
				local totalRadius = 30
				if(  (offsetX*offsetX + offsetY*offsetY) < totalRadius*totalRadius ) then
					enemy.status = "deleted"
				end	
			end
		end
	end
	-------------------------------------------------------------------
	-- BIRD
	-------------------------------------------------------------------
	if( params.type == "bird") then
		enemy = display.newImage( "Resource/bird.png",params.x,params.y )
		enemy.type = params.type
		enemy.radius = 10;
		enemy.status = "normal"
		enemyGroup:insert(enemy)
		
		enemy.speed = 120;
		-- public func
		function enemy:update()			
			if( enemy.status=="normal") then
				local offsetX = balloon.x-enemy.x;
				local offsetY = balloon.y-enemy.y;
				
				local len = math.sqrt(offsetX*offsetX + offsetY*offsetY)
				local totalRadius = enemy.radius + balloon.radius
				if(  len < totalRadius ) then
					balloon:onTouchEnemy(enemy)
				end				
				-- move the bird
				offsetX = offsetX / len
				offsetY = offsetY / len			
							
				if( offsetX>0 and  enemy.xScale==1 ) then
					enemy:scale( -1, 1 )				
				end			
				if( offsetX<=0 and  enemy.xScale==-1 ) then
					enemy:scale( -1, 1 )				
				end				
				
				--enemy.rotation =  -angle
				local angle = enemy:getAngle(offsetX,offsetY,30)
				if( len> 100) then
					angle = angle / (len/100)
				end
				if( enemy.xScale==1) then
					enemy.rotation =  -angle
				else
					enemy.rotation =  angle
				end
				
				offsetX = offsetX* enemy.speed / fps
				offsetY = offsetY* enemy.speed / fps
				
				enemy.x = enemy.x + offsetX + speed*0.5
				enemy.y = enemy.y + offsetY
			end
			
			if( enemy.status=="hitted") then				
				enemy.x = enemy.x + (enemy.hitted.x/3)
				enemy.y = enemy.y + (enemy.hitted.y/3)
			end
			
			if(enemy.x < travelDistance-100 or 
			   enemy.y > playableHeight+100 or 
			   enemy.y < -100)
			then
				enemy.status = "deleted"							
			end
			
		end
		
		function enemy:getAngle(x,y,max_angle)
			local len = math.sqrt(x*x + y*y)
			x = x/len
			y = y/len
			local angle = math.asin(y)*180
			if( angle < -max_angle ) then
				angle = -max_angle
			end
			if( angle > max_angle) then
				angle = max_angle
			end
			return angle
		end
		
		function enemy:onTouch(event)
			if( event.phase=="ended" ) then				
				if( enemy.firstHit~=nil and event.id == enemy.firstHit.id ) then
					enemy.firstHit = nil
				end
				return
			end
				
			local offsetX = enemy.x - event.x;
			local offsetY = enemy.y - event.y;
			local totalRadius = 30
			if(  (offsetX*offsetX + offsetY*offsetY) < totalRadius*totalRadius ) then
				if( enemy.firstHit == nil) then
					enemy.firstHit = {x=event.x,y=event.y, id = event.id}
				else
					if( event.id == enemy.firstHit.id) then
						local dx = event.x-enemy.firstHit.x
						local dy = event.y-enemy.firstHit.y						
						if( dx*dx + dy*dy > 20*20) then
							enemy.status = "hitted"
							local angle = enemy:getAngle(dx,dy,360)
							enemy.rotation = angle
							enemy.hitted = {x=dx,y=dy}
							print(dx,dy,angle)
						end
					end					
				end
			end	
		end
	end
	
	return enemy
end

----------------------------------------------------------------------
-- GameLoop Function
----------------------------------------------------------------------

function gameLoop( event )   	
	
	if(isDied or balloon==nil) then
		return
	end	
	
	local vx, vy = balloon:getLinearVelocity()
	
	vx = vx / fps
	local speed_factor = (balloon.x-travelDistance) / 450;
	if(speed_factor>1.0) then
		speed_factor = 1.0
	end
	if( vx > 0 ) then
		speed =  3 + vx *speed_factor;
	else
		speed = 3;
	end
	
	travelDistance = travelDistance+speed	
	
	handx = handx + speed	
	
	-- Playable height
	local newPlayableCurrentHeight = -1
	local realControllerY = playableHeightController.y-playableCurrentHeight
	if( realControllerY < playableUpBorder) then
		--offsetHeight = realControllerY-playableUpBorder
		newPlayableCurrentHeight = playableHeightController.y-playableUpBorder
	end
	
	if(realControllerY > playableDownBorder) then
		--offsetHeight = realControllerY-playableDownBorder
		newPlayableCurrentHeight = playableHeightController.y-playableDownBorder
	end
	
	if( newPlayableCurrentHeight >= 0 and
		newPlayableCurrentHeight <= (playableHeight - screenHeight) ) then
		
		playableCurrentHeight = newPlayableCurrentHeight
		handy = deviceY + playableCurrentHeight		
	end
	
	
	if  (balloon.x+170) < travelDistance or balloon.y > (playableHeight+130) or balloon.y < -130 then
		onDie()
		return
	end
	
	--update gameScreen
	updateGameScreen()
	--update balloon
	balloon:update()
	
	--update enemy
	for i = 1, enemyGroup.numChildren do		
		enemyGroup[i]:update()
	end
	
	local enemyNum = enemyGroup.numChildren
	for i = 1, enemyNum do		
		if( i<= enemyNum) then
			local enemy = enemyGroup[i]		
			if( enemy.status=="deleted" ) then						
				enemy:removeSelf()
				i = i-1
				enemyNum = enemyNum-1
			end
		end
	end	
	
	if( isControl ) then
		-- controlActor:applyForce( (handx - controlActor.x)*forceControlFactor, (handy-controlActor.y)*forceControlFactor, controlActor.x, controlActor.y )
		if(controlActor.tempJoint) then
			controlActor.tempJoint:setTarget( handx, handy )
		end
	end	
	
	-- HUD
	scoreText.x = 40
	scoreText.y = 30
	scoreText.text = math.floor(travelDistance/100)
end 

----------------------------------------------------------------------
-- handleTouch Function
----------------------------------------------------------------------
function handleTouch( event )  
	
	if( isDied ) then
		return
	end
	deviceX = event.x
	deviceY = event.y
	event.x = event.x + travelDistance
	event.y = event.y + playableCurrentHeight
	
	-- Main TOUCH : control touch
	if( isControl == true and controlTouchID == event.id ) then
		handx = event.x
		handy = event.y
	end	
	if( isControl == false ) then
		if( event.phase == "began") then		
			if( math.abs ( controlActor.x - event.x ) < 32 and	math.abs ( controlActor.y - event.y ) < 32 ) then
				isControl = true
				controlTouchID = event.id
				controlActor.tempJoint = physics.newJoint( "touch", controlActor, controlActor.x,controlActor.y )			
				controlActor.tempJoint.dampingRatio = 1.0
				controlActor.tempJoint.frequency = 1000		
				handx = event.x
				handy = event.y
				return
			end		
		end	
	end
	
	if( isControl and controlTouchID == event.id) then
		if( event.phase == "ended" or event.phase == "cancelled") then
			isControl = false
			controlActor:setLinearVelocity( 0, 0 )
			
			if(controlActor.tempJoint) then
				controlActor.tempJoint:removeSelf()
				controlActor.tempJoint = nil
				return
			end
		end
	end
	
	-- Another touch
	if( isControl == false or controlTouchID ~= event.id ) then		
		for i = 1, enemyGroup.numChildren do					
			enemyGroup[i]:onTouch(event)			
		end		
	end		
	
end 

function spawnSnow()
	local num = math.random (spawnMinCount,spawnMaxCount);
	for i=1, num do		
		local ex = math.random (travelDistance+ screenWidth + 10,travelDistance+ screenWidth + 50);
		local ey = math.random (0 ,playableHeight);
		newEnemy{ type="snow",
				  x= ex,
				  y= ey}
	end
	timer.performWithDelay(math.random (spawnMinTime,spawnMaxTime), spawnSnow )
end

function spawnBird()
	local num = math.random (1,2);
	for i=1, num do		
		local ex = math.random (travelDistance+ screenWidth + 10,travelDistance+ screenWidth + 50);
		local ey = math.random (0 ,playableHeight);
		newEnemy{ type="bird",
				  x= ex,
				  y= ey}
	end
	timer.performWithDelay(math.random (3000,6000), spawnBird )
end

initLevel()

Runtime:addEventListener( "memoryWarning", handleLowMemory )
Runtime:addEventListener( "enterFrame", gameLoop )
Runtime:addEventListener( "touch", handleTouch )
timer.performWithDelay(math.random (spawnMinTime,spawnMaxTime), spawnSnow )

timer.performWithDelay(math.random (3000,6000), spawnBird )